`timescale 1ns/1ps
`include "Func.v"
module fetch (
	input	clk,
	input	rst_n,

	input wire	keygen_start,
	input wire	enc_start,
	input wire	dec_start,
    input wire  [2:0]  mode,
	input wire 	cmd_start,

	input	inst_done,//

    output  reg [21:0] inst,

    output  reg keygen,enc,dec,cmd,
	output 	reg	keygen_done,
	output	reg	enc_done,
	output	reg	dec_done,
	output 	reg	cmd_done,
    output  reg Start_Decode,


    //ram
    input wire ram_write,
    input wire [ 6:0] ram_addr_write,
    input wire [21:0] ram_din_write
);


//**********************  state reg **************************//


reg [2:0] mode_reg;
always@(posedge clk or negedge rst_n) begin
    if(!rst_n) begin keygen <= 1'b0; enc <= 1'b0; dec <= 1'b0; cmd <= 1'b0;  end
    else if(keygen_start & ~keygen) keygen<=1'b1; 
    else if(keygen_done  & keygen)  keygen<=1'b0;
    else if(enc_start & ~enc)  enc<=1'b1; 
    else if(enc_done  &  enc)  enc<=1'b0;
    else if(dec_start & ~dec)  dec<=1'b1; 
    else if(dec_done  &  dec)  dec<=1'b0;
    else if(cmd_start & ~cmd)  cmd<=1'b1;
    else if(cmd_done  &  cmd)  cmd<=1'b0;
end

always@(posedge clk or negedge rst_n) begin
    if(!rst_n) mode_reg <= 3'b111;
    else mode_reg <= mode;
end

//************************************************************//

//****************** inst addr ***************// 
reg [9:0] inst_addr ;
always@(posedge clk or negedge rst_n)
begin
    if(!rst_n) inst_addr <= 10'd1023;
    else if(cmd_start)  inst_addr<=10'd1;
    else if(keygen_start) begin
        case(mode)
        `KEM_Kyber512   :   inst_addr<=10'd205;
        `KEM_Kyber768   :   inst_addr<=10'd1;
        `KEM_Kyber1024  :   inst_addr<=10'd323;
        `PKE_Kyber512   :   inst_addr<=10'd205;
        `PKE_Kyber768   :   inst_addr<=10'd1;
        `PKE_Kyber1024  :   inst_addr<=10'd323;
        default:    inst_addr<=10'd1023;
        endcase
    end
    else if(enc_start) begin
        case(mode)
        `KEM_Kyber512   :   inst_addr<=10'd231;
        `KEM_Kyber768   :   inst_addr<=10'd51;
        `KEM_Kyber1024  :   inst_addr<=10'd405;
        `PKE_Kyber512   :   inst_addr<=10'd636;
        `PKE_Kyber768   :   inst_addr<=10'd687;
        `PKE_Kyber1024  :   inst_addr<=10'd769;
        default:    inst_addr<=10'd1023;
        endcase
    end
    else if(dec_start) begin
        case(mode)
        `KEM_Kyber512   :   inst_addr<=10'd273;
        `KEM_Kyber768   :   inst_addr<=10'd120;
        `KEM_Kyber1024  :   inst_addr<=10'd510;
        `PKE_Kyber512   :   inst_addr<=10'd673;
        `PKE_Kyber768   :   inst_addr<=10'd751;
        `PKE_Kyber1024  :   inst_addr<=10'd869;
        default:    inst_addr<=10'd1023;
        endcase
    end
    else if((keygen|enc|dec|cmd) & inst_done) inst_addr <= inst_addr + 1'b1;
end
//*************************************************//





//*********************** inst_rom ****************************//
reg inst_done_reg; //对时序
wire rom_en;
wire [21:0]rom_inst;
//assign rom_en = ((keygen | enc | dec) & inst_done) | ~(keygen | enc | dec) & (keygen_start|enc_start|dec_start);
assign rom_en = (keygen | enc | dec) & inst_done_reg;
//Inst_ROM_22X1024_SPROM theinstrom(.clka(clk),.addra(inst_addr),.ena(rom_en),.douta(rom_inst));
ins_rom theinstrom(.clka(clk),.addra(inst_addr),.ena(rom_en),.douta(rom_inst));
//************************************************************//

//************************* inst_ram *******************//
wire ram_en;
wire [ 6:0] ram_addr;
wire [21:0] ram_inst;

assign ram_addr = ram_write ? ram_addr_write : inst_addr[6:0];
assign ram_en = ram_write | (inst_done_reg & cmd) ;  //写或读

//Inst_RAM_22x128_SP_RF theinstram(.clka(clk),.addra(ram_addr),.ena(ram_en),.wea(ram_write),.douta(ram_inst),.dina(ram_din_write));
ins_ram theinstram(.clka(clk),.ena(ram_en),.wea(ram_write),.addra(ram_addr),.dina(ram_din_write),.douta(ram_inst));

//************************************************************//


//****************************  inst  **********************//

reg start_fetch;
always@(posedge clk or negedge rst_n)
begin
    if(!rst_n) start_fetch<=1'b0;
    else start_fetch<=(keygen|enc|dec|cmd)&inst_done_reg;
end
always@(posedge clk or negedge rst_n)
begin
    if(!rst_n) inst_done_reg<=1'b0;
    else inst_done_reg <= (keygen|enc|dec|cmd)&inst_done | ~(keygen | enc | dec | cmd)&(keygen_start|enc_start|dec_start|cmd_start);
end

always@(posedge clk or negedge rst_n) begin
    if(!rst_n) inst <= `void_inst;
    else if(inst_done_reg)      inst <= `void_inst;
    else if(keygen_done |enc_done | dec_done) inst <= `void_inst;
    else if(start_fetch) begin
        if(cmd) inst <= ram_inst;
        else inst <= rom_inst;
    end
end

always@(posedge clk or negedge rst_n) begin
    if(!rst_n) Start_Decode <= 1'b0;
    else Start_Decode <= start_fetch|inst_done_reg;
end
//************************************************************//

//************************ done ********************************//
always@(posedge clk or negedge rst_n)
begin
    if(!rst_n) {keygen_done,enc_done,dec_done} <= 3'b000;
    else begin
        case(mode_reg)
        `KEM_Kyber512 : begin keygen_done <= (keygen & inst_addr == 10'd230); enc_done <= (enc & inst_addr == 10'd272); dec_done <= (dec & inst_addr == 10'd322); end 
        `KEM_Kyber768 : begin keygen_done <= (keygen & inst_addr == 10'd50);  enc_done <= (enc & inst_addr == 10'd119); dec_done <= (dec & inst_addr == 10'd204); end 
        `KEM_Kyber1024: begin keygen_done <= (keygen & inst_addr == 10'd404); enc_done <= (enc & inst_addr == 10'd509); dec_done <= (dec & inst_addr == 10'd635); end 
        `PKE_Kyber512 : begin keygen_done <= (keygen & inst_addr == 10'd230); enc_done <= (enc & inst_addr == 10'd672); dec_done <= (dec & inst_addr == 10'd686); end 
        `PKE_Kyber768 : begin keygen_done <= (keygen & inst_addr == 10'd50);  enc_done <= (enc & inst_addr == 10'd750); dec_done <= (dec & inst_addr == 10'd768); end 
        `PKE_Kyber1024: begin keygen_done <= (keygen & inst_addr == 10'd404); enc_done <= (enc & inst_addr == 10'd868); dec_done <= (dec & inst_addr == 10'd891); end 
        default :begin {keygen_done,enc_done,dec_done} <= 3'b000; end
        endcase
    end
end 
always@(posedge clk or negedge rst_n)
begin
    if(!rst_n) cmd_done<=1'b0;
    else cmd_done <= (cmd & start_fetch & ram_inst == `void_inst);
end


//**************************************************************//

endmodule


module inst_fetch_tb;

reg clk,rst_n,keygen_start,enc_start,dec_start,cmd_start;
reg [2:0] mode;
reg inst_done;
wire [21:0] inst;
wire keygen_done,dec_done,enc_done;
wire Start_Decode;

fetch  thefetch(
		.clk(clk),.rst_n(rst_n),
        .keygen_start(keygen_start),
		.enc_start(enc_start),
		.dec_start(dec_start),
        .cmd_start(cmd_start),
        .mode(mode),
		.inst_done(inst_done),
        .inst(inst),
  
		.keygen_done(keygen_done),.enc_done(enc_done),.dec_done(dec_done), .Start_Decode(Start_Decode),
        .cmd_done(),.ram_write(),.ram_addr_write(),.ram_din_write()
);
initial
begin
    {clk,rst_n,keygen_start,enc_start,dec_start,cmd_start} = 6'd0;
    inst_done = 0;
    #10 rst_n =1;
    #20 rst_n =0;
    #20 rst_n =1;
    #100 mode = `KEM_Kyber768;
    #100 keygen_start =1;
    #10  keygen_start =0;
    #200 inst_done =1;
    #10 inst_done =0;
    #200 inst_done =1;
    #10 inst_done =0;
    #200 inst_done =1;
    #10 inst_done =0;
    #200 inst_done =1;
    #10 inst_done =0;
    #1000;
    $stop;

end
always #5 clk = ~clk;

endmodule


